﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;

namespace Z1029.PhiPlayer.OpenSource
{
    public class LevelController : MonoBehaviour
    {
        public Transform m_level;
        public SpriteRenderer background;
        public SpriteRenderer[] maskBackground;
        public GameObject judgelinePrefab;
        public JudgementLine[] judgelines;
        public Transform progressBar;
        public Text musicNameText;
        public Text comboShow;
        public Text scoreText;
        public Text musicNameInInfo;
        public Text harderText;
        public Text levelInfoText;
        public NotesManager notesManager;
        //public JudgeControl judgeControl;
        public ProgressControl progressControl;
        public HitEffectManager hitEffectManager;
        public int combo;
        public float score;
        public int notesCount;
        public static LevelController instance;
        private float musicLength;
        public AudioSource musicSource;
        public static string musicPath;
        public int screenW, screenH;
        public static double nowTime;
        public Chart gameChart;
        public FCAP FCAPState;
        public float noteSizeOffset;

        public Note[] notes;
        int maxCombo;
        float notesHarder;
        float rankHarder;
        public int comboScore;

        public static float noteSize = 0.055f;
        public static float hitSoundsVolume = 0.6f;
        public static double musicOffset = 0;
        public static int dspBuffer = 256;

        private void Awake()
        {
            noteSize = PlayerPrefs.GetFloat("NoteSize", 0.055f);
            hitSoundsVolume = PlayerPrefs.GetFloat("HitSoundsVolume", 0.6f);
            musicOffset = (double)PlayerPrefs.GetFloat("MusicOffset", 0f);
            dspBuffer = PlayerPrefs.GetInt("DspBuffer", 256);
            Application.targetFrameRate = 90;
            LoadChart(LevelInfo.currentInfo.chart);
            musicOffset -= gameChart.offset;

            if (instance != null)
            {
                throw new Exception("The <LevelController> is already instanced");
            }
            else
                instance = this;

            nowTime = 0;
            if (!LevelInfo.currentInfo.LoadFromResources)
            {
                musicLength = LevelInfo.music.length;
                musicSource.clip = LevelInfo.music;
            }
            else
            {
                string ResourcesPath = LevelInfo.currentInfo.ResourcesPath;
                foreach (FileInfo fi in (new DirectoryInfo(ResourcesPath)).GetFiles())
                {
                    string fullName = fi.FullName;
                    if (fullName.EndsWith(".wav"))
                    {
                        StartCoroutine(LoadMusic(fullName, AudioType.WAV));
                        goto MusicLoaded;
                    }
                    else if (fullName.EndsWith(".mp2") || (fullName.EndsWith(".mp3")))
                    {
                        StartCoroutine(LoadMusic(fullName, AudioType.MPEG));
                        goto MusicLoaded;
                    }
                    else if (fullName.EndsWith(".ogg"))
                    {
                        StartCoroutine(LoadMusic(fullName, AudioType.OGGVORBIS));
                        goto MusicLoaded;
                    }
                    else if (fullName.EndsWith(".aiff"))
                    {
                        StartCoroutine(LoadMusic(fullName, AudioType.AIFF));
                        goto MusicLoaded;
                    }
                    else if (fullName.EndsWith(".vag"))
                    {
                        StartCoroutine(LoadMusic(fullName, AudioType.VAG));
                        goto MusicLoaded;
                    }
                    else if (fullName.EndsWith(".mod"))
                    {
                        StartCoroutine(LoadMusic(fullName, AudioType.MOD));
                        goto MusicLoaded;
                    }
                    else if (fullName.EndsWith(".s3m"))
                    {
                        StartCoroutine(LoadMusic(fullName, AudioType.S3M));
                        goto MusicLoaded;
                    }
                    else if (fullName.EndsWith(".it"))
                    {
                        StartCoroutine(LoadMusic(fullName, AudioType.IT));
                        goto MusicLoaded;
                    }
                }
                Utils.AndroidToastStringShow("Music file not found");
                Application.LoadLevelAsync(0);
                throw new FileNotFoundException("MusicFileNotFound");
            }

        MusicLoaded:
            screenH = Screen.height > 1080 ? 1080 : Screen.height;
            screenW = Screen.width > 1920 ? 1920 : Screen.width;
            Screen.autorotateToLandscapeLeft = true;
            Screen.autorotateToLandscapeRight = true;
            Screen.autorotateToPortrait = false;
            Screen.autorotateToPortraitUpsideDown = false;
            Screen.orientation = ScreenOrientation.AutoRotation;

            noteSizeOffset = (((1920 / 1080) / 10f) - 0.177777777777777f) / 2.25f + 0.17777777777777f;
            bool ipad = screenH * 1.3f > screenW;
            if (ipad) noteSizeOffset -= 0.025f;

            noteSizeOffset *= 1.25f;
        }

        public IEnumerator LoadMusic(string path, AudioType audioType)
        {
            path = "file:///" + path;
            Debug.Log(path);
            UnityWebRequest uwr = UnityWebRequestMultimedia.GetAudioClip(path, audioType);
            uwr.SendWebRequest();
            AudioClip music;
            yield return uwr;
            if (uwr.isDone)
            {
                try
                {
                    if (uwr.error == null)
                    {
                        music = DownloadHandlerAudioClip.GetContent(uwr);
                        musicSource.clip = music;
                        musicLength = music.length;


                        rankHarder = notesHarder / musicLength;
                        rankHarder = rankHarder * 2f + 5f;
                        rankHarder = rankHarder > 15f ? rankHarder * 0.9f : rankHarder;
                        rankHarder = rankHarder > 17f ? 17 : rankHarder;


                        if (musicLength == 0)
                        {
                            Utils.AndroidToastStringShow($"MusicLoadErr, Path:\"{path}\"");
                            Application.LoadLevel(0);
                        }
                        OverUI.rankHarder = rankHarder;
                        Debug.Log("RankHarder = " + rankHarder);
                        //LevelController.music = www.GetAudioClip();
                    }
                    else
                    {
                        Debug.LogError(uwr.error);
                        Utils.AndroidToastStringShow(uwr.error + $", Path:\"{path}\"");
                        Application.LoadLevel(0);
                    }
                }
                catch
                {
                    Debug.LogError(uwr.error);
                    Utils.AndroidToastStringShow(uwr.error + $", Path:\"{path}\"");
                    Application.LoadLevel(0);
                }
            }
        }

        private void LoadChart(string chart)
        {
            bool ipad = screenH * 1.3f > screenW;
            if (ipad) print("Ipad Screen");
            gameChart = ChartReader.ReadChart(chart);
            if (ipad)
            {
                for (int i = 0; i < gameChart.judgeLineList.Length; i++)
                {
                    for (int j = 0; j < gameChart.judgeLineList[i].moveEvents.Length; j++)
                    {
                        gameChart.judgeLineList[i].moveEvents[j].start = gameChart.judgeLineList[i].moveEvents[j].end / 9 * 7;
                    }
                    for (int j = 0; j < gameChart.judgeLineList[i].notesAbove.Length; j++)
                    {
                        gameChart.judgeLineList[i].notesAbove[j].positionX = gameChart.judgeLineList[i].notesAbove[j].positionX / 9 * 7;
                    }
                    for (int j = 0; j < gameChart.judgeLineList[i].notesBelow.Length; j++)
                    {
                        gameChart.judgeLineList[i].notesBelow[j].positionX = gameChart.judgeLineList[i].notesBelow[j].positionX / 9 * 7;
                    }
                }
            }
        }

        private void Start()
        {
            //music.Play();
            AudioSettings.SetDSPBufferSize(dspBuffer, dspBuffer);

            LevelInfo levelInfo = LevelInfo.currentInfo;
            musicNameText.text = levelInfo.musicName;
            musicNameInInfo.text = levelInfo.musicName;
            levelInfoText.text = string.Format("Illustration designed by {0}\nLevel designed by {1}", levelInfo.levelInfo.Split(',')[0], levelInfo.levelInfo.Split(',')[1]);
            harderText.text = levelInfo.levelHarderLv.ToString() + " Lv." + ((levelInfo.levelHarder == -1) ? "?" : levelInfo.levelHarder.ToString());
            background.sprite = levelInfo.illustrationBlur;
            background.color = new Color(1, 1, 1, PlayerPrefs.GetFloat("BackgroundBrightness", 0.5f));
            for (int i = 0; i < maskBackground.Length; i++)
            {
                maskBackground[i].sprite = levelInfo.illustrationBlur;
                //maskBackground.
            }
            OverUI.harder = levelInfo.levelHarderLv.ToString() + " Md5." + levelInfo.levelMd5;
            OverUI.level = levelInfo.musicName;
            OverUI.illustration = levelInfo.illustration;
            OverUI.illustrationBlur = levelInfo.illustrationBlur;
            OverUI.badCount = 0;
            OverUI.perfectCount = 0;
            OverUI.missCount = 0;
            OverUI.goodCount = 0;

            SetJudgeLines();
            SetNotes();

            GC.Collect();
        }

        public void StartSyncMusicTime()
        {
            EnableJudgelines(true);
            progressControl.StartAudio();
        }

        public void UpdateNote()
        {
            for (int i = 0; i < notes.Length; i++)
            {
                if (notes[i] != null)
                {
                    float y = notes[i].CalculateDistance();
                    {
                        if ((y <= 20 || notes[i].noteData.time - 1 < nowTime) && (y >= 0 || notes[i].noteData.time < nowTime))
                        {
                            if (!notes[i].gameObject.activeSelf) notes[i].gameObject.SetActive(true);
                        }
                        else if (notes[i].gameObject.activeSelf) notes[i].gameObject.SetActive(false);
                    }
                }
            }
        }

        public void UpdateCombo()
        {
            if (combo > 2)
            {
                comboShow.gameObject.SetActive(true);
                comboShow.text = combo.ToString();
            }
            else
            {
                comboShow.gameObject.SetActive(false);
            }
            if (combo > maxCombo)
            {
                maxCombo = combo;
            }
        }

        public void UpdateScore()
        {
            scoreText.text = string.Format("{0:0000000}", CalculateScore());
        }

        public int CalculateScore()
        {
            return Mathf.RoundToInt((score / notesCount * 900000f) + ((float)maxCombo / notesCount) * 100000);
            //return Mathf.RoundToInt((score / notesCount * 900000f) + ((float)comboScore / ((1 + notesCount) * notesCount / 2) * 100000));
        }

        public void AddScore(Note.HitACC acc)
        {
            switch (acc)
            {
                case Note.HitACC.Perfect:
                    combo++;
                    comboScore += instance.combo;
                    score += 1f;
                    OverUI.perfectCount++;
                    break;
                case Note.HitACC.Good:
                    combo++;
                    comboScore += instance.combo;
                    score += 0.6f;
                    OverUI.goodCount++;
                    if (FCAPState == FCAP.AllPerfect)
                        FCAPState = FCAP.FullCombo;
                    break;
                case Note.HitACC.Bad:
                    combo = 0;
                    OverUI.badCount++;
                    FCAPState = FCAP.None;
                    break;
                case Note.HitACC.Miss:
                    FCAPState = FCAP.None;
                    OverUI.missCount++;
                    break;
                default: Debug.LogError("Note not judged!!!"); break;
            }
        }

        private void Update()
        {
            UpdateCombo();
            UpdateScore();

            if (!PauseGame.pause && PauseGame.start)
            {
                UpdateNote();
                progressBar.localPosition = new Vector3((float)nowTime / musicLength * 1920f - 1920f, progressBar.localPosition.y, progressBar.localPosition.z);
                if (nowTime + 0.1f > musicLength)
                {
                    StartCoroutine(EndLevel());
                    PauseGame.start = false;
                    GetComponent<PauseGame>().End();
                }
            }
        }

        IEnumerator EndLevel()
        {
            yield return new WaitForSeconds(3f);
            OverUI.newScore = CalculateScore();
            OverUI.maxCombo = maxCombo;
            Application.LoadLevel("OverMenu");
        }

        private void SetJudgeLines()
        {
            judgelines = new JudgementLine[gameChart.judgeLineList.Length];
            for (int i = 0; i < gameChart.judgeLineList.Length; i++)
            {
                Transform lineObj = Instantiate(judgelinePrefab.transform);
                judgelines[i] = lineObj.GetComponent<JudgementLine>();
                lineObj.localScale = Vector3.one;
                lineObj.parent = m_level;
                JudgementLine line = judgelines[i];
                line.bpm = gameChart.judgeLineList[i].bpm;
                line.speeds = gameChart.judgeLineList[i].speedEvents;
                line.moveEvents = gameChart.judgeLineList[i].moveEvents;
                line.rotateEvents = gameChart.judgeLineList[i].rotateEvents;
                line.alphaEvents = gameChart.judgeLineList[i].alphaEvents;
                //int count = gameChart.judgeLineList[i].notesAbove.Length + gameChart.judgeLineList[i].notesBelow.Length;
            }
            EnableJudgelines(false);
        }

        private void SetNotes()
        {
            notesCount = 0;
            notes = new Note[gameChart.numOfNotes];
            notesHarder = 0;
            for (int i = 0; i < judgelines.Length; i++)
            {
                for (int j = 0; j < gameChart.judgeLineList[i].notesAbove.Length; j++)
                {
                    Note.NoteData data = gameChart.judgeLineList[i].notesAbove[j];
                    data.index = notesCount;
                    notes[notesCount] = notesManager.InstantiateNote(gameChart.judgeLineList[i].notesAbove[j].type, data, judgelines[i], Note.Direction.Above);
                    notesCount++;
                    if (data.type == NoteType.Click || data.type == NoteType.Hold)
                    {
                        notesHarder++;
                    }
                    else if (data.type == NoteType.Flick)
                    {
                        notesHarder += 0.2f;
                    }
                }
                for (int k = 0; k < gameChart.judgeLineList[i].notesBelow.Length; k++)
                {
                    Note.NoteData data = gameChart.judgeLineList[i].notesBelow[k];
                    data.index = notesCount;
                    notes[notesCount] = notesManager.InstantiateNote(gameChart.judgeLineList[i].notesBelow[k].type, data, judgelines[i], Note.Direction.Below);
                    notesCount++;
                    if (data.type == NoteType.Click || data.type == NoteType.Hold)
                    {
                        notesHarder++;
                    }
                    else if (data.type == NoteType.Flick)
                    {
                        notesHarder += 0.2f;
                    }
                }
            }
            if (notesCount != gameChart.numOfNotes)
            {
                Utils.AndroidToastStringShow("Chart read error : Note count error");
                Application.LoadLevel(0);
            }
            for (int j = 0; j < notes.Length; j++)
            {
                notes[j].transform.position = new Vector3(100000, 0, 0);
                notes[j].SetNoteHighLight();
                notes[j].gameObject.SetActive(false);
            }
        }

        public void EnableJudgelines(bool enable)
        {
            for (int i = 0; i < judgelines.Length; i++)
            {
                judgelines[i].enabled = enable;
                judgelines[i].UpdateJudgelineColor();
            }
        }

        public enum FCAP
        {
            AllPerfect, FullCombo, None
        }
    }
}